// ==UserScript==
// @name           Enlarge facebook profile images
// @namespace      MattiasBlom
// @version 1.0 by Michael Coyne
// @version 1.1 by Mattias Blom: attach mouseover images at "profile.ak.facebook.com", forget links.
// @version 1.2 by Johan Sundström: cleanups, handle any (not just profile) photo, Instant Gallery plugin.
// @description    Show full sized pictures when hovering over profile or photo album thumbnails on Facebook.
// @include        http://*.facebook.com/*
// ==/UserScript==

// http://userscripts.org/scripts/show/11446 original by Michael Coyne
// http://userscripts.org/scripts/show/12433 refactored by Mattias Blom
// - attach mouseover images at "profile.ak.facebook.com", forget links.
// this version further still refactored by Johan Sundström on 2007-10-21
// - cleanups, handle any (not just profile) photo, Instant Gallery plugin.

var big;
var thumbnail_regexp = /[qst](\d+[^\/]+)$/;
var background_regexp = /background[^;]*url\(['"]?(http.*?)["']?\)/;

function init() {
  function is_thumb(img) {
    return (imageurl(img)||"").match(thumbnail_regexp);
  }
  big = document.createElement("img");
  big.style.position = 'absolute';
  big.style.zIndex = 10000;
  big.style.visibility = 'hidden';
  document.body.appendChild(big);

  $x('(//img[(' +
       'starts-with(@src,"http://photos-") or ' +
       'starts-with(@src,"http://profile.")' +
     ') and (' +
       'contains(@src,".ak.facebook.com/") or ' +
       'contains(@src,".ll.facebook.com/")'+
     ')]) | (//a[(' +
       'contains(@style,"http://photos-") or ' +
       'contains(@style,"http://profile.")' +
     ') and (' +
       'contains(@style,".ak.facebook.com/") or ' +
       'contains(@style,".ll.facebook.com/")'+
     ')])').filter( is_thumb ).map( onhover );
}

function onhover( img ) {
  var url = imageurl(img);
  if (!url) return;
  img.addEventListener('mouseout', unzoom, false);
  img.addEventListener('mouseover', zoom, false);
  img.addEventListener('mousemove', reposition, false);

  // Plug in to Instant Gallery, http://userscripts.org/scripts/show/3791
  var a = document.createElement("a");
  a.href = url.replace(thumbnail_regexp, "n$1");
  a.style.display = "none";
  img.parentNode.appendChild( a );
}

function unzoom() {
  big.src = "";
  big.style.visibility = "hidden";
}

function zoom( e ) {
  var url = imageurl(e.target);
  if (url) {
    big.src = url.replace(thumbnail_regexp, "n$1");
  }
}

// from img extract src,
// from style="background-image: url(...);", extract "..."
// (used on http://www.facebook.com/statusupdates/ for instance)
function imageurl( node ) {
  var url = node.src;
  if (url) return url;
  url = node.getAttribute("style").match(background_regexp);
  if (url) return url[1];
}

function reposition( e ) {
  wait( function() { return e.clientX && big.clientHeight && big.clientWidth },
        function() { show( e ); } );
}

function show( e ) {
  var x = e.clientX, y = e.clientY;
  var h = big.clientHeight, w = big.clientWidth;
  big.style.top = window.scrollY+y-(h/2)+'px';

  var ymin = scrollY;
  var ymax = ymin + innerHeight - h;
  var ynow = parseInt( big.style.top, 10 );
  if (h > innerHeight)
    big.style.top = (ymin + (innerHeight - h)/2) + "px";
  else if (ynow < ymin)
    big.style.top = ymin + "px";
  else if (ynow > ymax)
    big.style.top = ymax + "px";

  if (innerWidth - 15 <= scrollX + x + w + 25)
    big.style.left = (x - 25 + scrollX - w) + "px";
  else
    big.style.left = (x + 25 + scrollX) + "px";

  big.style.visibility = 'visible';
}

// Utility Functions

function $x( xpath ) {
  var doc = document;
  var got = doc.evaluate( xpath, doc, null, 0, null ), next, result = [];
  while (next = got.iterateNext())
    result.push( next );
  return result;
}

function wait( test, cb ) {
  if (test())
    return cb();
  setTimeout(wait, 300, test, cb);
}

init();
